Ext.ux.Portlet = Ext.extend(Ext.Panel, {
            anchor : '100%',
            collapsible : true,
            draggable : true,
            cls : 'x-portlet'
        });
Ext.reg('portlet', Ext.ux.Portlet);
Ext.ux.PortalColumn = Ext.extend(Ext.Container, {
            layout : 'anchor',
            autoEl : 'div',
            defaultType : 'portlet',
            cls : 'x-portal-column'
        });
Ext.reg('portalcolumn', Ext.ux.PortalColumn);
Ext.ux.Portal = Ext.extend(Ext.Panel, {
            layout : 'column',
            autoScroll : true,
            cls : 'x-portal',
            defaultType : 'portalcolumn',
            initComponent : function() {
                Ext.ux.Portal.superclass.initComponent.call(this);
                this.addEvents({
                    validatedrop : true,
                    beforedragover : true,
                    dragover : true,
                    beforedrop : true,
                    drop : true
                });
            },
            initEvents : function() {
                Ext.ux.Portal.superclass.initEvents.call(this);
                this.dd = new Ext.ux.Portal.DropZone(this,
                        this.dropConfig);
            },
            beforeDestroy : function() {
                if (this.dd) {
                    this.dd.unreg();
                }
                Ext.ux.Portal.superclass.beforeDestroy.call(this);
            }
        });
Ext.reg('portal', Ext.ux.Portal);
Ext.ux.Portal.DropZone = function(portal, cfg) {
    this.portal = portal;
    Ext.dd.ScrollManager.register(portal.body);
    Ext.ux.Portal.DropZone.superclass.constructor.call(this,
            portal.bwrap.dom, cfg);
    portal.body.ddScrollConfig = this.ddScrollConfig;
};
Ext.extend(Ext.ux.Portal.DropZone, Ext.dd.DropTarget, {
    ddScrollConfig : {
        vthresh : 50,
        hthresh : -1,
        animate : true,
        increment : 200
    },
    createEvent : function(dd, e, data, col, c, pos) {
        return {
            portal : this.portal,
            panel : data.panel,
            columnIndex : col,
            column : c,
            position : pos,
            data : data,
            source : dd,
            rawEvent : e,
            status : this.dropAllowed
        };
    },
    notifyOver : function(dd, e, data) {
        var xy = e.getXY(), portal = this.portal, px = dd.proxy;
        // case column widths
        if (!this.grid) {
            this.grid = this.getGrid();
        }
        // handle case scroll where scrollbars appear during drag
        var cw = portal.body.dom.clientWidth;
        if (!this.lastCW) {
            this.lastCW = cw;
        } else if (this.lastCW != cw) {
            this.lastCW = cw;
            portal.doLayout();
            this.grid = this.getGrid();
        }
        // determine column
        var col = 0, xs = this.grid.columnX, cmatch = false;
        for (var len = xs.length; col < len; col++) {
            if (xy[0] < (xs[col].x + xs[col].w)) {
                cmatch = true;
                break;
            }
        }
        // no match, fix last index
        if (!cmatch) {
            col--;
        }
        // find insert position
        var p, match = false, pos = 0, c = portal.items.itemAt(col), items = c.items.items;
        for (var len = items.length; pos < len; pos++) {
            p = items[pos];
            var h = p.el.getHeight();
            if (h !== 0 && (p.el.getY() + (h / 2)) > xy[1]) {
                match = true;
                break;
            }
        }
        var overEvent = this.createEvent(dd, e, data, col, c, match && p
                        ? pos
                        : c.items.getCount());
        if (portal.fireEvent('validatedrop', overEvent) !== false
                && portal.fireEvent('beforedragover', overEvent) !== false) {
            // make sure proxy width is fluid
            px.getProxy().setWidth('auto');
            if (p) {
                px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
            } else {
                px.moveProxy(c.el.dom, null);
            }
            this.lastPos = {
                c : c,
                col : col,
                p : match && p ? pos : false
            };
            this.scrollPos = portal.body.getScroll();
            portal.fireEvent('dragover', overEvent);
            return overEvent.status;;
        } else {
            return overEvent.status;
        }
    },
    notifyOut : function() {
        delete this.grid;
    },
    notifyDrop : function(dd, e, data) {
        delete this.grid;
        if (!this.lastPos) {
            return;
        }
        var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;
        var dropEvent = this.createEvent(dd, e, data, col, c, pos !== false
                        ? pos
                        : c.items.getCount());
        if (this.portal.fireEvent('validatedrop', dropEvent) !== false
                && this.portal.fireEvent('beforedrop', dropEvent) !== false) {
            dd.proxy.getProxy().remove();
            dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
            if (pos !== false) {
                c.insert(pos, dd.panel);
            } else {
                c.add(dd.panel);
            }
            /**
             * 取得当前portlet在portal column中的索引
             */
            var index = 0;
            var portletId = dd.panel.id;
            if (pos != false) {
                var portlets = c.items;
                for (var i = 0; i < portlets.length; i++) {
                    if (c.items.itemAt(i).id === portletId) {
                        index = i;
                        break;
                    }
                }
            } else {
                if (pos.toString() == '0') {
                    index = 0;
                } else {
                    index = c.items.length - 1;
                }
            }

            c.doLayout();
            //更新移动后的Portlet
            var params ={
                id : dd.panel.id,
                columnid:c.id,
                forder : index
            };
            indexportalService.movePortlet(params);

            this.portal.fireEvent('drop', dropEvent);
            // scroll position is lost on drop, fix it
            var st = this.scrollPos.top;
            if (st) {
                var d = this.portal.body.dom;
                setTimeout(function() {
                            d.scrollTop = st;
                        }, 10);
            }
        }
        delete this.lastPos;
    },
    // internal cache of body and column coords
    getGrid : function() {
        var box = this.portal.bwrap.getBox();
        box.columnX = [];
        this.portal.items.each(function(c) {
            box.columnX.push({
                x : c.el.getX(),
                w : c.el.getWidth()
            });
        });
        return box;
    },
    // unregister the dropzone from ScrollManager
    unreg : function() {
        // Ext.dd.ScrollManager.unregister(this.portal.body);
        Ext.ux.Portal.DropZone.superclass.unreg.call(this);
    }
});